﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using System.Linq;
using System.Data;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace EvoGame.Ceeforee.Helpers
{
	[Serializable]
	internal class C4EDBFile
	{
		public Dictionary<string, string> TxtDB = new Dictionary<string, string>();
		public Dictionary<string, Civilization> CivDB = new Dictionary<string,Civilization>();
		public Dictionary<string, Building> BuildDB = new Dictionary<string, Building>();
		public Dictionary<string, Unit> UnitDB = new Dictionary<string, Unit>();

		public bool isEmpty = true;

		public string FilePath
		{
			get { return string.Format("{0}\\C4EDB_{1}.ebf", filePath, lang); }
			set { filePath = value; }
		}
		private string lang, filePath;

		public C4EDBFile(Lang l, string f)
		{
			lang = l.ToString();
			filePath = f;
			UnitDB.Add("NONE", new Unit());
			BuildDB.Add("NONE", new Building());
		}
	}

	public class C4EDB
	{
		public MainWin mWin;

		private C4EDBFile civ4;
		private C4EDBFile civX;
		private C4EDBFile mod;

		private FileStream DBFile;
		BinaryFormatter binFormat = new BinaryFormatter();

		//		private Dictionary<string, string> txtDB = new Dictionary<string, string>();
		//		private Dictionary<string, Civilization> civDB = new Dictionary<string,Civilization>();
		//		private Dictionary<string, Building> buildDB = new Dictionary<string, Building>();
		//		private Dictionary<string, Unit> unitDB = new Dictionary<string, Unit>();

		private C4EDB(MainWin mw)
		{
			mWin = mw;
		}

		public static C4EDB Load(MainWin mw)
		{
			C4EDB db = new C4EDB(mw);
			db.civ4 = db.LoadFile(mw.Language, mw.LoadedMod.Dir["Civ4"]["Root"]);
			if (db.civ4.isEmpty || db.CheckForUpdatedFiles(db.civ4.FilePath))
			{
				db.civ4 = db.LoadXML("Civ4");
				db.SaveFile(db.civ4);
			}
			db.civX = db.LoadFile(mw.Language, mw.LoadedMod.Dir["CivX"]["Root"]);
			if (db.civX.isEmpty || db.CheckForUpdatedFiles(db.civX.FilePath))
			{
				db.civX = db.LoadXML("CivX");
				db.SaveFile(db.civX);
			}
			db.mod = db.LoadFile(mw.Language, mw.LoadedMod.Dir["Mod"]["Root"]);
			if (db.mod.isEmpty || db.CheckForUpdatedFiles(db.mod.FilePath))
			{
				db.mod = db.LoadXML("Mod");
				db.SaveFile(db.mod);
			}
			return db;
		}

		private C4EDBFile LoadFile(Lang l, string f)
		{
			C4EDBFile o = new C4EDBFile(l, f);
			try
			{
				DBFile = new FileStream(o.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
			}
			catch (FileNotFoundException)
			{
				string message = string.Format("Database file \"{0}\" not found, Creating new Database file", o.FilePath);
				MessageBox.Show(message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Information);
				mWin.Debug = string.Format("Displayed message:{0}\t{1}", Environment.NewLine, message);
				SaveFile(o);
				C4EDBFile tmp = LoadFile(l, f);
				return tmp;
			}
			C4EDBFile output = (C4EDBFile)binFormat.Deserialize(DBFile);
			DBFile.Close();
			return output;
		}

		private C4EDBFile LoadXML(string version)
		{
			C4EDBFile output = new C4EDBFile(mWin.Language, mWin.LoadedMod.Dir[version]["Root"]);
			//output.mWin = mWin;
			string tmpDir;
			XDocument tmpFile;
			List<XElement> tmpKeys = new List<XElement>();

			#region TXT_KEY Data
			tmpDir = String.Format("{0}\\Text", mWin.LoadedMod.Dir[version]["XML"]);
			//if (mWin.LoadedMod.Modular) tmpDir = String.Format("{0}\\Modules", mWin.LoadedMod.Dir[version]["Assets"]);
			if (Directory.Exists(tmpDir))
			{
				foreach (FileInfo file in new DirectoryInfo(tmpDir).GetFiles("*.xml", SearchOption.AllDirectories))
				{
					tmpFile = XDocument.Load(file.FullName);
					tmpKeys.AddRange(tmpFile.Root.Elements());
				}
				foreach (XElement xe in tmpKeys)
				{
					string tag = Helper.XML.GetStringData(xe, "Tag");

					if (output.TxtDB.ContainsKey(tag))
					{
						output.TxtDB[tag] = Helper.XML.GetStringData(xe, mWin.Language.ToString());
					}
					else
					{
						output.TxtDB.Add(tag, Helper.XML.GetStringData(xe, mWin.Language.ToString()));
					}
				}
			}
			tmpKeys.Clear();
			#endregion

			#region UNIT Data
			tmpDir = String.Format("{0}\\Units", mWin.LoadedMod.Dir[version]["XML"]);
			if (mWin.LoadedMod.Modular) tmpDir = String.Format("{0}\\Modules", mWin.LoadedMod.Dir[version]["Assets"]);
			if (Directory.Exists(tmpDir))
			{
				foreach (FileInfo file in new DirectoryInfo(tmpDir).GetFiles("*CIV4UnitInfos.xml", SearchOption.AllDirectories))
				{
					tmpFile = XDocument.Load(file.FullName);
					tmpKeys.AddRange(tmpFile.Root.Elements().Elements<XElement>());
				}
				foreach (XElement xe in tmpKeys)
				{
					Unit unit = new Unit();

					unit.ClassType = Helper.XML.GetStringData(xe, "Class");
					unit.Type = Helper.XML.GetStringData(xe, "Type");
					//unit.SpecialType = Helper.XML.GetStringData(xe, "SpecialBuildingType");
					//unit.Description = Helper.XML.GetStringData(xe, "Description");
					//unit.Civilopedia = Helper.XML.GetStringData(xe, "Civilopedia");
					//unit.Strategy = Helper.XML.GetStringData(xe, "Strategy");
					//unit.Advisor = Helper.XML.GetStringData(xe, "Advisor");
					//unit.ArtDefineTag = Helper.XML.GetStringData(xe, "ArtDefineTag");
					//unit.MovieDefineTag = Helper.XML.GetStringData(xe, "MovieDefineTag");
					//unit.HolyCity = Helper.XML.GetStringData(xe, "HolyCity");
					//unit.ReligionType = Helper.XML.GetStringData(xe, "ReligionType");

					if (output.UnitDB.ContainsKey(unit.Type))
					{
						output.UnitDB[unit.Type] = unit;
					}
					else
					{
						output.UnitDB.Add(unit.Type, unit);
					}
				}
			}
			tmpKeys.Clear();
			#endregion

			#region BUILDING Data
			tmpDir = String.Format("{0}\\Buildings", mWin.LoadedMod.Dir[version]["XML"]);
			if (mWin.LoadedMod.Modular) tmpDir = String.Format("{0}\\Modules", mWin.LoadedMod.Dir[version]["Assets"]);
			if (Directory.Exists(tmpDir))
			{
				foreach (FileInfo file in new DirectoryInfo(tmpDir).GetFiles("*CIV4BuildingInfos.xml", SearchOption.AllDirectories))
				{
					tmpFile = XDocument.Load(file.FullName);
					tmpKeys.AddRange(tmpFile.Root.Elements().Elements<XElement>());
				}
				foreach (XElement xe in tmpKeys)
				{
					Building build = new Building();

					build.ClassType = Helper.XML.GetStringData(xe, "BuildingClass");
					build.Type = Helper.XML.GetStringData(xe, "Type");
					build.SpecialType = Helper.XML.GetStringData(xe, "SpecialBuildingType");
					build.Description = Helper.XML.GetStringData(xe, "Description");
					build.Civilopedia = Helper.XML.GetStringData(xe, "Civilopedia");
					build.Strategy = Helper.XML.GetStringData(xe, "Strategy");
					build.Advisor = Helper.XML.GetStringData(xe, "Advisor");
					build.ArtDefineTag = Helper.XML.GetStringData(xe, "ArtDefineTag");
					build.MovieDefineTag = Helper.XML.GetStringData(xe, "MovieDefineTag");
					build.HolyCity = Helper.XML.GetStringData(xe, "HolyCity");
					build.ReligionType = Helper.XML.GetStringData(xe, "ReligionType");

					if (output.BuildDB.ContainsKey(build.Type))
					{
						output.BuildDB[build.Type] = build;
					}
					else
					{
						output.BuildDB.Add(build.Type, build);
					}
				}
			}
			tmpKeys.Clear();
			#endregion

			#region CIVILIZATION Data
			tmpDir = String.Format("{0}\\Civilizations", mWin.LoadedMod.Dir[version]["XML"]);
			if (mWin.LoadedMod.Modular) tmpDir = String.Format("{0}\\Modules", mWin.LoadedMod.Dir[version]["Assets"]);
			if (Directory.Exists(tmpDir))
			{
				foreach (FileInfo file in new DirectoryInfo(tmpDir).GetFiles("*CIV4CivilizationInfos.xml", SearchOption.AllDirectories))
				{
					tmpFile = XDocument.Load(file.FullName);
					tmpKeys.AddRange(tmpFile.Root.Elements().Elements<XElement>());
				}
				foreach (XElement xe in tmpKeys)
				{
					Civilization civ = new Civilization();

					if (version == "Mod")
						civ.Modified = true;

					civ.Type = Helper.XML.GetStringData(xe, "Type");
					civ.Description = Helper.XML.GetStringData(xe, "Description");
					civ.ShortDescription = Helper.XML.GetStringData(xe, "ShortDescription");
					civ.Adjective = Helper.XML.GetStringData(xe, "Adjective");
					civ.Civilopedia = Helper.XML.GetStringData(xe, "Civilopedia");
					civ.DefaultPlayerColor = Helper.XML.GetStringData(xe, "DefaultPlayerColor");
					civ.ArtDefineTag = Helper.XML.GetStringData(xe, "ArtDefineTag");
					civ.ArtStyleType = Helper.XML.GetStringData(xe, "ArtStyleType");
					civ.UnitArtStyleType = Helper.XML.GetStringData(xe, "UnitArtStyleType");
					civ.Playable = Helper.XML.GetBoolData(xe, "bPlayable");
					civ.AIPlayable = Helper.XML.GetBoolData(xe, "bAIPlayable");
					civ.Cities = Helper.XML.GetRawData(xe, "Cities").Elements().ToList<XElement>().ConvertAll<string>(x => x.Value);
					foreach (XElement x in Helper.XML.GetRawData(xe, "Buildings").Elements())
					{
						if (x.HasElements)
						{
							civ.UniqueBuildings.Add(GetBuild(Helper.XML.GetStringData(x, "BuildingType")));
						}
					}
					foreach (XElement x in Helper.XML.GetRawData(xe, "FreeBuildingClasses").Elements())
					{
						civ.StartingBuildings.AddRange(GetBuildByClass(Helper.XML.GetStringData(x, "BuildingClassType")));
					}

					foreach (XElement x in Helper.XML.GetRawData(xe, "Units").Elements())
					{
						if (x.HasElements)
						{
							civ.UniqueUnits.Add(GetUnit(Helper.XML.GetStringData(x, "UnitType")));
						}
					}
					foreach (XElement x in Helper.XML.GetRawData(xe, "FreeUnitClasses").Elements())
					{
						civ.StartingUnits.AddRange(GetUnitByClass(Helper.XML.GetStringData(x, "UnitClassType")));
					}

					civ.DerivedCiv = Helper.XML.GetStringData(xe, "DerivativeCiv");
					civ.SelectionSound = Helper.XML.GetStringData(xe, "CivilizationSelectionSound");
					civ.ActionSound = Helper.XML.GetStringData(xe, "CivilizationActionSound");

					if (output.CivDB.ContainsKey(civ.Type))
					{
						output.CivDB[civ.Type] = civ;
					}
					else
					{
						output.CivDB.Add(civ.Type, civ);
					}
				}
			}
			tmpKeys.Clear();
			#endregion

			output.isEmpty = false;
			return output;
		}

		public void Save()
		{
			SaveFile(mod);
		}

		private void SaveFile(C4EDBFile f)
		{
			DBFile = new FileStream(f.FilePath, FileMode.Create, FileAccess.Write, FileShare.Read);
			binFormat.Serialize(DBFile, f);
			DBFile.Close();
		}

		//private void SaveXML();

		private bool CheckForUpdatedFiles(string f)
		{
			FileInfo file = new FileInfo(f);
			DateTime fileWrite = file.LastWriteTimeUtc;
			DateTime dirWrite = file.Directory.LastWriteTimeUtc;
			if (fileWrite < dirWrite) { return true; }
			return false;
		}

		public string GetTXTKey(string key)
		{
			if (mod.TxtDB.ContainsKey(key))
			{
				return mod.TxtDB[key];
			}
			else if (civX.TxtDB.ContainsKey(key))
			{
				return civX.TxtDB[key];
			}
			else if (civ4.TxtDB.ContainsKey(key))
			{
				return civ4.TxtDB[key];
			}
			else
			{
				mWin.Debug = string.Format("Key \"{0}\" is missing", key);
				return key;
			}
		}

		public Civilization GetCiv(string key)
		{
			if (mod.CivDB.ContainsKey(key))
			{
				return mod.CivDB[key];
			}
			else if (civX.CivDB.ContainsKey(key) && mWin.LoadedMod.Modular)
			{
				return civX.CivDB[key];
			}
			else if (civ4.CivDB.ContainsKey(key) && mWin.LoadedMod.Modular)
			{
				return civ4.CivDB[key];
			}
			else
			{
				mWin.Debug = string.Format("Civ \"{0}\" is missing", key);
				return null;
			}
		}

		public Building GetBuild(string key)
		{
			{
				if (mod.BuildDB.ContainsKey(key))
				{
					return mod.BuildDB[key];
				}
				else if (civX.BuildDB.ContainsKey(key) && mWin.LoadedMod.Modular)
				{
					return civX.BuildDB[key];
				}
				else if (civ4.BuildDB.ContainsKey(key) && mWin.LoadedMod.Modular)
				{
					return civ4.BuildDB[key];
				}
				else
				{
					mWin.Debug = string.Format("Civ \"{0}\" is missing", key);
					return null;
				}
			}
		}

		public Unit GetUnit(string key)
		{
			{
				if (mod.UnitDB.ContainsKey(key))
				{
					return mod.UnitDB[key];
				}
				else if (civX.UnitDB.ContainsKey(key) && mWin.LoadedMod.Modular)
				{
					return civX.UnitDB[key];
				}
				else if (civ4.UnitDB.ContainsKey(key) && mWin.LoadedMod.Modular)
				{
					return civ4.UnitDB[key];
				}
				else
				{
					mWin.Debug = string.Format("Civ \"{0}\" is missing", key);
					return null;
				}
			}
		}

		public List<string> GetCivNames() { return GetCivNames(false); }
		public List<string> GetCivNames(bool raw)
		{
			List<string> tmp = new List<string>();
			foreach (Civilization civ in mod.CivDB.Values.ToList<Civilization>())
			{
				if (raw)
				{
					tmp.Add(civ.Type);
				}
				else
				{
					tmp.Add(GetTXTKey(civ.ShortDescription));
				}
			}
			if (mWin.LoadedMod.Modular)
			{
				foreach (Civilization civ in civX.CivDB.Values.ToList<Civilization>())
				{
					if (raw)
					{
						tmp.Add(civ.Type);
					}
					else
					{
						if (!tmp.Contains(GetTXTKey(civ.ShortDescription)))
							tmp.Add(GetTXTKey(civ.ShortDescription));
					}
				}

				foreach (Civilization civ in civ4.CivDB.Values.ToList<Civilization>())
				{
					if (raw)
					{
						tmp.Add(civ.Type);
					}
					else
					{
						if (!tmp.Contains(GetTXTKey(civ.ShortDescription)))
							tmp.Add(GetTXTKey(civ.ShortDescription));
					}
				}
			}
			return tmp;
		}

		public List<Building> GetBuildByClass(string key)
		{
			List<Building> modSB = (from b in mod.BuildDB where b.Value.ClassType == key select b.Value).ToList<Building>();
			List<Building> civXSB = (from b in civX.BuildDB where b.Value.ClassType == key select b.Value).ToList<Building>();
			List<Building> civ4SB = (from b in civ4.BuildDB where b.Value.ClassType == key select b.Value).ToList<Building>();
			return modSB.Union<Building>(civXSB, new Building.Comparer()).Union<Building>(civ4SB, new Building.Comparer()).ToList<Building>();
		}

		public List<Unit> GetUnitByClass(string key)
		{
			List<Unit> modSB = (from b in mod.UnitDB where b.Value.ClassType == key select b.Value).ToList<Unit>();
			List<Unit> civXSB = (from b in civX.UnitDB where b.Value.ClassType == key select b.Value).ToList<Unit>();
			List<Unit> civ4SB = (from b in civ4.UnitDB where b.Value.ClassType == key select b.Value).ToList<Unit>();
			return modSB.Union<Unit>(civXSB, new Unit.Comparer()).Union<Unit>(civ4SB, new Unit.Comparer()).ToList<Unit>();
		}

		public void AddTXTKey(string key, string tag)
		{
			if (mod.TxtDB.ContainsKey(key))
			{
				mod.TxtDB[key] = tag;
			}
			else
			{
				mod.TxtDB.Add(key, tag);
			}
		}

		public void AddCiv(Civilization civ)
		{
			if (mod.CivDB.ContainsKey(civ.Type))
			{
				mod.CivDB[civ.Type] = civ;
			}
			else
			{
				mod.CivDB.Add(civ.Type, civ);
			}
		}

		public void AddBuild(Building build)
		{
			if (mod.BuildDB.ContainsKey(build.Type))
			{
				mod.BuildDB[build.Type] = build;
			}
			else
			{
				mod.BuildDB.Add(build.Type, build);
			}
		}
	}
}